<?php
/**
 * @see http://www.ietf.org/rfc/rfc2616.txt
 */
namespace TwinPeaks\Core\Http;
use TwinPeaks\Core as Core;

class CResponse
{
	use Core\TAppInjector;
	
	const HEADER_LOCATION_INDEX = 'location';
	const HEADER_CONTENT_TYPE_INDEX = 'content_type';

	/**
	 * @var
	 */
	private $httpVersion = 1.1;
	
	/**
	 * @var integer
	 */
	private $statusCode;
	
	/**
	 * @var string
	 */
	private $reasonPhrase;
	
	/**
	 * @var string
	 */
	private $body;
	
	/**
	 * @var array
	 */
	private $httpHeaders = [];
	
	/**
	 * @var CUri
	 */
	private $locationURI;
	
	/**
	 * @var string
	 */
	private $mediaType;
	
	/**
	 * @var string
	 */
	private $mediaTypeEncoding;

	/**
	 * @var array
	 */
	private $cookies = [];	

	/**
	 * @final
	 * @access public
	 * @return CResponse
	 */
	final static function createFromHeader($header)
	{
		$fields = explode("\r\n", $header);
		// The first line of a Response message is the Status-Line, consisting
		// of the protocol version followed by a numeric status code and its
		// associated textual phrase, with each element separated by SP
		// characters. No CR or LF is allowed except in the final CRLF sequence.

		// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF		
		$statusLine = array_shift($fields);
		$matches = null;
		if (!preg_match('|^HTTP\/(\d\.\d) (\d+) (.*)$|i', $statusLine, $matches))
			throw new Exception();
		
		$res = new static();
		$res->statusCode = intval($matches[2]);
		$res->reasonPhrase = $matches[3];
		
		return $res;
	}

	/**
	 * @return integer
	 */
	function getStatusCode()
	{
		return $this->statusCode;
	}
	
	/**
	 * @return CResponse
	 */
	function setStatusCode($code)
	{
		$this->statusCode = $code;
		
		return $this;
	}
	
	/**
	 * @return integer Expiration date as Unix time
	 */
	function setCookie($name, $value, $expireMixed = 0, $path = '/', $domain = '', $secure = '', $httpOnly = '')
	{
		if (is_string($expireMixed))
		{ // Specify expire date using relative format.
			$dt = new \DateTime('now');
			$dt->modify($expireMixed);
			$expire = $dt->format('U');			
		}
		else
			$expire = $expireMixed; // Unix time
				
		$this->cookies[$name] = [
			'value' => $value,
			'expire' => $expire,
			'path' => $path,
			'domain' => $domain,
			'secure' => $secure,
			'http-only' => $httpOnly
		];
		
		return $expire;
	}
	
	/**
	 * @return void
	 */
	function removeCookie($name)
	{
		$this->setCookie($name, '', '-1 day');
	}
	
	
	/**
	 * @return void
	 */
	protected function sendHeader()
	{
		if (isset($this->statusCode))
		{
			$statusLine = $this->getStatusLine($this->statusCode);
			header($statusLine);
			if ($this->statusCode == 302 || $this->statusCode == 303)
				header('Location: ' . $this->locationURI->toString());
		}
		
		foreach ($this->cookies as $name => $params)
			setcookie($name, $params['value'], $params['expire'],
				$params['path'], $params['domain'], $params['secure'],
				$params['http-only']
			);
		
		if (isset($this->mediaType))
			header($this->getContentTypeHeader());
	}
	
	/**
	 * @return string
	 */
	final function getStatusLine($statusCode)
	{
		$statusLine = 'HTTP/' . $this->httpVersion . ' ';
		$statusLine .= $this->statusCode . ' ';
		$statusLine .= $this->getReasonPhrase($statusCode);
		return $statusLine;
	}
	
	/**
	 * @return void
	 */
	function dispatch()
	{
		$app = self::getApplication();
		
		if ($app->getOption(Core\OPT_DISABLE_REQUEST_DISPATCH))
			return;
		
		$outBuffer = '';
		while (ob_get_level())
			$outBuffer .= ob_get_clean();

		if (strlen($outBuffer))
			$app->warning("PHP output buffer is not empty: %s", $outBuffer);
		
		$this->sendHeader();
		$app->_echo($this->getBody());
		
		exit;
	}
	
	/**
	 * @return string
	 */
	function getBody()
	{
		return $this->body;
	}
	
	/**
	 * @return $this
	 */
	function setBody($body)
	{
		$this->body = $body;
		return $this;
	}
    
	/**
	 * @return string
	 */
	final function getReasonPhrase($statusCode)
	{
		static $phrase = [
			STATUS_CODE_NOT_FOUND => 'Not found'
		];
		if (isset($phrase[$statusCode]))
			return $phrase[$statusCode];
		else
			return '';
	}	

	/**
	 * @reutrn string
	 */
	final function getMediaType()
	{
		return $this->mediaType;
	}
	
	/**
	 * @return void
	 */
	final function setMediaType($type, $encoding = 'UTF-8')
	{
		$this->mediaType = $type;
		$this->mediaTypeEncoding = $encoding;
	}

	/**
	 * @return string
	 */
	final function getContentTypeHeader()
	{
		return RESHF_CONTENT_TYPE . ': ' . $this->mediaType .
			'; charset=' . $this->mediaTypeEncoding;
	}
	
	/**
	 * @return void
	 */
	final function redirect($URI, $code = 303)
	{
		$this->statusCode = $code;
		$this->locationURI = $URI;
	}
}
